#include <iostream>
#include "../includes/need-a-name.hh"
#include "../includes/project-base.hh"
#include "../includes/subproject-base.hh"
#include "../includes/equation-base.hh"
#include "../includes/init-base.hh"
#include "../includes/boundary-base.hh"

#include "../equations/includes/constant-advection.hh"
#include "../equations/includes/sound-wave.hh"
#include "../equations/includes/euler.hh"
#include "../equations/includes/mhd.hh"
#include "../initcollecter/includes/initcollecter-constant-advection.hh"
#include "../initcollecter/includes/initcollecter-sound-wave.hh"
#include "../initcollecter/includes/initcollecter-euler.hh"
#include "../initcollecter/includes/initcollecter-mhd.hh"

#include "includes/project_fv_3D_YASP.hh"

namespace fv3d{

class constadv : public SubprojectBase{
public:
  void set_subproject_signature(){
    subproject_signature="constadv_lf";
  }
  void assign_equation(){
    std::cout<<"assign constant advection equation"<<std::endl;
    equation=new ConstantAdvection();
  }
  void assign_initialcondition(){
    init=new InitCollecterConstantAdvection();
  }
  void assign_boundarycondition(){
    boundary=new BoundaryBase();
  }
  void set_unique_parameters(){
    std::cout<<"set unique parameters"<<std::endl;
    gridsetting::lower[0]=0.0;
    gridsetting::upper[0]=1.0;
    gridsetting::lower[1]=0.0;
    gridsetting::upper[1]=1.0;    
    gridsetting::lower[2]=0.0;
    gridsetting::upper[2]=1.0;    
    gridsetting::element[0]=40;
    gridsetting::element[1]=40;
    gridsetting::element[2]=40;    
    timerule::start=0.0;
    timerule::end=0.3;    
    equation->set_parameter("u0",1.0);
    equation->set_parameter("v0",1.0);
    equation->set_parameter("w0",1.0);
    init->set_parameter("cen1",0.5);
    init->set_parameter("cen2",0.5);    
    init->set_parameter("cen3",0.5);    
    init->set_parameter("wid1",0.2);
    init->set_parameter("wid2",0.2);
    init->set_parameter("wid3",0.2);    
    init->set_parameter("r",0.282842712475);        
    init->set_parameter("rectangle",0.0);
    init->set_parameter("circle",0.0);
    set_unique2_parameters();
  }
  virtual void set_unique2_parameters(){
  }
  void dataanalysis(){
  }
};

class constadv_rect_lf : public constadv{
  void set_subproject_signature(){
    subproject_signature="constadv_rect_lf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("rectangle",1.0);    
    setting::spacediscrete = setting::lf;    
  }
};

class constadv_rect_tvdlf : public constadv{
  void set_subproject_signature(){
    subproject_signature="constadv_rect_tvdlf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("rectangle",1.0);    
    setting::spacediscrete = setting::tvdlf;    
  }
};

class constadv_circle_lf : public constadv{
  void set_subproject_signature(){
    subproject_signature="constadv_circle_lf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("circle",1.0);    
    setting::spacediscrete = setting::lf;    
  }
};

class constadv_circle_tvdlf : public constadv{
  void set_subproject_signature(){
    subproject_signature="constadv_circle_tvdlf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("circle",1.0);    
    setting::spacediscrete = setting::tvdlf;    
  }
};

class sounwav : public SubprojectBase{
public:
  void set_subproject_signature(){
    subproject_signature="sounwav_lf";
  }
  void assign_equation(){
    std::cout<<"assign sound wave equation"<<std::endl;
    equation=new SoundWave();
  }
  void assign_initialcondition(){
    init=new InitCollecterSoundWave();
  }
  void assign_boundarycondition(){
    boundary=new BoundaryBase();
  }
  void set_unique_parameters(){
    std::cout<<"set unique parameters"<<std::endl;
    gridsetting::lower[0]=0.0;
    gridsetting::upper[0]=1.0;
    gridsetting::lower[1]=0.0;
    gridsetting::upper[1]=1.0;
    gridsetting::lower[2]=0.0;
    gridsetting::upper[2]=1.0;
    gridsetting::element[0]=60;
    gridsetting::element[1]=60;
    gridsetting::element[2]=60;        
    timerule::start=0.0;
    timerule::end=0.3;    
    equation->set_parameter("u0",0.0);
    equation->set_parameter("v0",0.0);
    equation->set_parameter("w0",0.0);    
    equation->set_parameter("rho0",1.0);
    equation->set_parameter("k0",2.0);        

    init->set_parameter("rectangle",0.0);
    init->set_parameter("cen1",0.5);
    init->set_parameter("cen2",0.5);
    init->set_parameter("cen3",0.5);        
    init->set_parameter("wid1",0.2);
    init->set_parameter("wid2",0.2);
    init->set_parameter("wid3",0.2);    
    init->set_parameter("r",0.282842712475);
    init->set_parameter("k1",1.0);
    init->set_parameter("k2",1.0);
    init->set_parameter("k3",1.0);    

    init->set_parameter("plane",0.0);
    init->set_parameter("circle",0.0);

    dataanalysis::auxnumber=0;
    set_unique2_parameters();
  }
  virtual void set_unique2_parameters(){
    //    std::cout<<"virtual unique2 parameters"<<std::endl;        
  }
  void dataanalysis(){
  }
};
class sounwav_plane_tvdlf : public sounwav{
  void set_subproject_signature(){
    subproject_signature="sounwav_plane_tvdlf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("rho_plane",1.0);    
    setting::spacediscrete = setting::tvdlf;    
  }
};
class sounwav_plane_lf : public sounwav{
  void set_subproject_signature(){
    subproject_signature="sounwav_plane_lf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("rho_plane",1.0);    
    setting::spacediscrete = setting::lf;    
  }
};

class sounwav_circle_tvdlf : public sounwav{
  void set_subproject_signature(){
    subproject_signature="sounwav_circle_tvdlf";
  }  
  void set_unique2_parameters(){
    // std::cout<<"call unique2"<<std::endl;    
    init->set_parameter("rho_circle",1.0);
    setting::spacediscrete = setting::tvdlf;    
  }
};
class sounwav_circle_lf : public sounwav{
  void set_subproject_signature(){
    subproject_signature="sounwav_circle_lf";
  }  
  void set_unique2_parameters(){
    std::cout<<"call unique2"<<std::endl;
    init->set_parameter("rho_circle",1.0);
    setting::spacediscrete = setting::lf;    
  }
};

class euler : public SubprojectBase{
public:
  void set_subproject_signature(){
    subproject_signature="euler";
  }
  void assign_equation(){
    std::cout<<"assign euler equation"<<std::endl;
    equation=new Euler();
  }
  void assign_initialcondition(){
    init=new InitCollecterEuler();
  }
  void assign_boundarycondition(){
    boundary=new BoundaryBase();
  }
  void set_unique_parameters(){
    std::cout<<"set unique parameters"<<std::endl;
    gridsetting::lower[0]=0.0;
    gridsetting::upper[0]=1.0;
    gridsetting::lower[1]=0.0;
    gridsetting::upper[1]=1.0;
    gridsetting::lower[2]=0.0;
    gridsetting::upper[2]=1.0;
    gridsetting::element[0]=60;
    gridsetting::element[1]=60;
    gridsetting::element[2]=60;    	 
    timerule::start=0.0;
    timerule::end=0.3;    
    // init->set_parameter("para2",1.0);
    dataanalysis::auxnumber=0;
    // std::cout<<"before unique2 parameters"<<std::endl;    
    set_unique2_parameters();
  }
  virtual void set_unique2_parameters(){
    //    std::cout<<"virtual unique2 parameters"<<std::endl;        
  }
  void dataanalysis(){
  }
};

class euler_sod_lf : public euler{
  void set_subproject_signature(){
    subproject_signature="euler_sod_lf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("sod_shocktube",1.0);
    setting::spacediscrete = setting::lf;    
  }
};

class euler_blast_lf : public euler{
  void set_subproject_signature(){
    subproject_signature="euler_blast_lf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("blastwave",1.0);
    setting::spacediscrete = setting::lf;    
  }
};


class euler_sod_tvdlf : public euler{
  void set_subproject_signature(){
    subproject_signature="euler_sod_tvdlf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("sod_shocktube",1.0);
    setting::spacediscrete = setting::tvdlf;    
  }
};

class euler_blast_tvdlf : public euler{
  void set_subproject_signature(){
    subproject_signature="euler_blast_tvdlf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("blastwave",1.0);
    setting::spacediscrete = setting::tvdlf;    
  }
};

  
class mhd : public SubprojectBase{
public:
  void set_subproject_signature(){
    subproject_signature="mhd";
  }
  void assign_equation(){
    std::cout<<"assign mhd equation"<<std::endl;
    equation=new MHD();
  }
  void assign_initialcondition(){
    init=new InitCollecterMHD();
  }
  void assign_boundarycondition(){
    boundary=new BoundaryBase();
  }
  void set_unique_parameters(){
    std::cout<<"set unique parameters"<<std::endl;
    gridsetting::lower[0]=0.0;
    gridsetting::upper[0]=1.0;
    gridsetting::lower[1]=0.0;
    gridsetting::upper[1]=1.0;
    gridsetting::lower[2]=0.0;
    gridsetting::upper[2]=1.0;    
    gridsetting::element[0]=60;
    gridsetting::element[1]=60;
    gridsetting::element[2]=60;        
    timerule::start=0.0;
    timerule::end=0.3;    
    equation->set_parameter("para1",0.0);
    init->set_parameter("para2",1.0);
    dataanalysis::auxnumber=0;
    // std::cout<<"before unique2 parameters"<<std::endl;    
    set_unique2_parameters();
  }
  virtual void set_unique2_parameters(){
    //    std::cout<<"virtual unique2 parameters"<<std::endl;        
  }
  void dataanalysis(){
  }
};

class mhd_blast_lf : public mhd{
  void set_subproject_signature(){
    subproject_signature="mhd_blast_lf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("blastwave",1.0);
    setting::spacediscrete = setting::lf;    
  }
};

class mhd_blast_tvdlf : public mhd{
  void set_subproject_signature(){
    subproject_signature="mhd_blast_tvdlf";
  }  
  void set_unique2_parameters(){
    init->set_parameter("blastwave",1.0);
    setting::spacediscrete = setting::tvdlf;    
  }
};
  
}

//-----------------------------------------
Project_fv_3D_YASP::Project_fv_3D_YASP(){ 
}
Project_fv_3D_YASP::~Project_fv_3D_YASP(){
}
void Project_fv_3D_YASP::set_project_signature(){
  project_signature="fv_3D_YASP";
}
void Project_fv_3D_YASP::set_common_parameters(int subproj){
  // Grid and dimension need to be assigned in driver-file.cc and setting.hh
  // Grid: #define YASP
  // dimension: const int dimension=1
  setting::timeadvance=setting::euler;
  setting::boundary=setting::constant;
  timerule::dt=1.0E-100;
  timerule::current=0.0;
  timerule::counter=0.0;
  timerule::cfl=0.8;
  memory::dgorder=0;
  // boundary setting
  for(int tmpp=0; tmpp<memory::phynumber; tmpp++)
    for(int tmpd=0; tmpd<gridsetting::dimension; tmpd++){
      get_subproject(subproj)->boundary->left[tmpp][tmpd]="const";
      get_subproject(subproj)->boundary->left[tmpp][tmpd]="const";      
    }
}

void Project_fv_3D_YASP::load_subprojects(){
  // add_subproject(new fv3d::constadv_rect_lf());
  // add_subproject(new fv3d::constadv_rect_tvdlf());    
  // add_subproject(new fv3d::constadv_circle_lf());
  // add_subproject(new fv3d::constadv_circle_tvdlf());

   add_subproject(new fv3d::sounwav_plane_lf());
   add_subproject(new fv3d::sounwav_plane_tvdlf());
   add_subproject(new fv3d::sounwav_circle_lf());
   add_subproject(new fv3d::sounwav_circle_tvdlf());  

  // add_subproject(new fv3d::euler_sod_lf());
  // add_subproject(new fv3d::euler_sod_tvdlf());
  // add_subproject(new fv3d::euler_blast_lf());
  // add_subproject(new fv3d::euler_blast_tvdlf());    
  
  // add_subproject(new fv3d::mhd_blast_lf());
  // add_subproject(new fv3d::mhd_blast_tvdlf());        
};
